前篇介紹MVC使用HttpHandler是MvcHandler透過並MvcRouteHandler物件來返回.
我有做一個可以針對於Asp.net MVC Debugger的專案,只要下中斷點就可輕易進入Asp.net MVC原始碼.
大家介紹如何取得Controller執行物件
在ProcessRequest方法是透過ProcessRequestInit取得執行controller物件,讓我們看看是這個方法如何controller物件.
private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory)
{
HttpContext currentContext = HttpContext.Current;
if (currentContext != null)
{
bool? isRequestValidationEnabled = ValidationUtility.IsValidationEnabled(currentContext);
if (isRequestValidationEnabled == true)
{
ValidationUtility.EnableDynamicValidation(currentContext);
}
}
AddVersionHeader(httpContext);
RemoveOptionalRoutingParameters();
// Get the controller type
string controllerName = RequestContext.RouteData.GetRequiredString("controller");
// Instantiate the controller and call Execute
factory = ControllerBuilder.GetControllerFactory();
controller = factory.CreateController(RequestContext, controllerName);
if (controller == null)
{
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentCulture,
MvcResources.ControllerBuilder_FactoryReturnedNull,
factory.GetType(),
controllerName));
}
}
從上面程式碼可以得知我們執行Controller物件實現於IController介面,並會呼叫IController.Execute方法.
IController介面是同步的方式執行。為了支持非同步請求處理,IController介面非同步版本System.Web.Mvc.IAsyncController被定義出来。IAsyncController介面通過BeginExecute/EndExecute方法组合来完成。
public interface IController
{
void Execute(RequestContext requestContext);
}
public interface IAsyncController : IController
{
IAsyncResult BeginExecute(RequestContext requestContext, AsyncCallback callback, object state);
void EndExecute(IAsyncResult asyncResult);
}
透過RouteData.GetRequiredString取得執行Controller名稱,經由RouteValueDictionary查找之前註冊Url樣板並解析此次要使用Controller名稱
public string GetRequiredString(string valueName)
{
object obj;
if (this.Values.TryGetValue(valueName, out obj))
{
string str = obj as string;
if (!string.IsNullOrEmpty(str))
return str;
}
throw new InvalidOperationException(string.Format((IFormatProvider) CultureInfo.CurrentUICulture, System.Web.SR.GetString("RouteData_RequiredValue"), new object[1]
{
(object) valueName
}));
}
ControllerBuilder類別定義一個Current靜態只讀屬性現在返回ControllerBuilder物件是一個全域物件。SetControllerFactory方法重載用於註冊ControllerFactory類型或物件,而GetControllerFactory方法返回一個具體ControllerFactory物件。
我們透過GetControllerFactory取得返回Controller工廠.
public class ControllerBuilder
{
public IControllerFactory GetControllerFactory();
public void SetControllerFactory(Type controllerFactoryType);
public void SetControllerFactory(IControllerFactory controllerFactory);
IControllerFactory GetControllerFactory();
public HashSet<string> DefaultNamespaces { get; }
public static ControllerBuilder Current { get; }
}
GetControllerFactory透過private IResolver<IControllerFactory>取得要執行的ControllerFactory.
一般來說沒有設置就是使用DefaultControllerFactory工廠來取得Controller物件
public IControllerFactory GetControllerFactory()
{
return _serviceResolver.Current;
}
internal ControllerBuilder(IResolver<IControllerFactory> serviceResolver)
{
_serviceResolver = serviceResolver ?? new SingleServiceResolver<IControllerFactory>(
() => _factoryThunk(),
new DefaultControllerFactory { ControllerBuilder = this },
"ControllerBuilder.GetControllerFactory");
}
IControllerFactory介面有三個方法.
CreateController取得Controller物件(工廠模式最重要方法)GetControllerSessionBehavior取得Session
ASP.NET Session狀態行為。ReleaseController釋放使用資源public interface IControllerFactory
{
IController CreateController(RequestContext requestContext, string controllerName);
SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName);
void ReleaseController(IController controller);
}
既然知道透過哪個工廠來產生Controller我們繼續追工廠是如何產生Controller物件
GetControllerType取得要執行Controller類型GetControllerInstance取得Controller物件並返回使用public virtual IController CreateController(RequestContext requestContext, string controllerName)
{
if (requestContext == null)
{
throw new ArgumentNullException("requestContext");
}
if (String.IsNullOrEmpty(controllerName) && !requestContext.RouteData.HasDirectRouteMatch())
{
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
}
Type controllerType = GetControllerType(requestContext, controllerName);
IController controller = GetControllerInstance(requestContext, controllerType);
return controller;
}
GetControllerInstance通過反射(系統不會對建立的Controller進行快取
使用
IControllerActivator(預設DefaultControllerActivator) 來建立Controller物件
protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
throw new HttpException(404,
String.Format(
CultureInfo.CurrentCulture,
MvcResources.DefaultControllerFactory_NoControllerFound,
requestContext.HttpContext.Request.Path));
}
if (!typeof(IController).IsAssignableFrom(controllerType))
{
throw new ArgumentException(
String.Format(
CultureInfo.CurrentCulture,
MvcResources.DefaultControllerFactory_TypeDoesNotSubclassControllerBase,
controllerType),
"controllerType");
}
//使用IControllerActivator(預設DefaultControllerActivator) 來建立Controller物件
return ControllerActivator.Create(requestContext, controllerType);
}
上面說GetControllerInstance會透過一個ControllerActivator,而ControllerActivator預設其實是DefaultControllerActivator類別幫助我們建立Controller物件透過Create方法.
以下是DefaultControllerActivator程式碼
private class DefaultControllerActivator : IControllerActivator
{
private Func<IDependencyResolver> _resolverThunk;
public DefaultControllerActivator()
: this(null)
{
}
public DefaultControllerActivator(IDependencyResolver resolver)
{
if (resolver == null)
{
_resolverThunk = () => DependencyResolver.Current;
}
else
{
_resolverThunk = () => resolver;
}
}
public IController Create(RequestContext requestContext, Type controllerType)
{
try
{
return (IController)(_resolverThunk().GetService(controllerType) ?? Activator.CreateInstance(controllerType));
}
catch (Exception ex)
{
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentCulture,
MvcResources.DefaultControllerFactory_ErrorCreatingController,
controllerType),
ex);
}
}
}
能看到這邊依賴一個IDependencyResolver,這裡先埋個小伏筆後面幾篇會為各位解答.
DefaultControllerActivator透過Activator.CreateInstance產生Controller物件,使用無建構子參數的Create方式
今天我們學到如何取得Controller執行物件
IControllerFactory工廠物件取得Controller執行物件,對於外部提供可替換點.RouteData.GetRequiredString取得執行的Controller名稱DefaultControllerFactory透過反射方式動態建立物件.工廠模式主要核心把如何使用物件跟如何建立物件中間解耦合,使用方不關心如何產生物件,只專注於此物件可執行的能力(介面)
下圖是本次介紹類別UML關係圖

MvcHandler是MVC的核心類別,借由ControllerBuilder創件者來取得產生Controller的工廠(預設使用DefaultControllerFactory),並呼叫CreateController方法來產生一個Controller